/******************************************************************************
	Cobalt - utility libraries for building XNA games
	Copyright (C) 2008 Philip Djonov

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2 of the License, or (at your option)
	any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
	for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the
	
		Free Software Foundation, Inc.
		51 Franklin Street, Fifth Floor
		Boston, MA  02110-1301, USA.
******************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler;

namespace Cobalt.Content.Pipeline.Quake.Bsp
{
	public abstract class BspItem
	{
		internal BspItem() { }

		private int index = -1;
		public int Index
		{
			get { return index; }
			set { index = value; }
		}
	}

	public class BrushSide
	{
		public BrushSide() { }

		private Plane plane;
		public Plane Plane
		{
			get { return plane; }
			set { plane = value; }
		}

		private int surfaceFlags;
		public int SurfaceFlags
		{
			get { return surfaceFlags; }
			set { surfaceFlags = value; }
		}
	}

	public class Brush : BspItem
	{
		public Brush() { }

		private List<BrushSide> sides = new List<BrushSide>();
		public List<BrushSide> Sides
		{
			get { return sides; }
		}

		private int contentFlags;
		public int ContentFlags
		{
			get { return contentFlags; }
			set { contentFlags = value; }
		}
	}

	#region Geometry and Surface
	public class GeometryBuffer : BspItem
	{
		public GeometryBuffer()
		{
		}

		public GeometryBuffer( VertexBufferContent vertices, VertexElement[] vertexDeclaration, IndexCollection indices )
		{
			this.vertices = vertices;
			this.vertexDeclaration = vertexDeclaration;
			this.indices = indices;
		}

		private VertexElement[] vertexDeclaration;
		public VertexElement[] VertexDeclaration
		{
			get { return vertexDeclaration; }
			set { vertexDeclaration = value; }
		}

		private VertexBufferContent vertices;
		public VertexBufferContent Vertices
		{
			get { return vertices; }
			set { vertices = value; }
		}

		private IndexCollection indices;
		public IndexCollection Indices
		{
			get { return indices; }
			set { indices = value; }
		}	
	}

	public class Surface : BspItem
	{
		public Surface( GeometryBuffer buffer )
		{
			this.buffer = buffer;
		}

		private GeometryBuffer buffer;
		public GeometryBuffer Buffer
		{
			get { return buffer; }
			set { buffer = value; }
		}

		private MaterialContent material;
		public MaterialContent Material
		{
			get { return material; }
			set { material = value; }
		}

		private int vertexCount;
		public int VertexCount
		{
			get { return vertexCount; }
			set { vertexCount = value; }
		}

		private int firstVertex;
		public int FirstVertex
		{
			get { return firstVertex; }
			set { firstVertex = value; }
		}

		private int indexCount;
		public int IndexCount
		{
			get { return indexCount; }
			set { indexCount = value; }
		}

		private int firstIndex;
		public int FirstIndex
		{
			get { return firstIndex; }
			set { firstIndex = value; }
		}
	}
	#endregion

	#region Nodes
	public abstract class BspNode : BspItem
	{
		internal BspNode() { }

		private BspNode parent;
		public BspNode Parent
		{
			get { return parent; }
			internal set { parent = value; }
		}

		private BoundingBox bounds;
		public BoundingBox Bounds
		{
			get { return bounds; }
			set { bounds = value; }
		}

		private List<Surface> surfaces = new List<Surface>();
		public List<Surface> Surfaces
		{
			get { return surfaces; }
		}
	}

	public class BspPartitionNode : BspNode
	{
		private BspNode child0;
		public BspNode Child0
		{
			get { return child0; }
			set
			{
				if( child0 == value )
					return;

				if( child0 != null )
					child0.Parent = null;

				child0 = value;

				if( child0 != null )
					child0.Parent = this;
			}
		}

		private BspNode child1;
		public BspNode Child1
		{
			get { return child1; }
			set
			{
				if( child1 == value )
					return;

				if( child1 != null )
					child1.Parent = null;

				child1 = value;

				if( child1 != null )
					child1.Parent = this;
			}
		}

		private Plane plane;
		public Plane Plane
		{
			get { return plane; }
			set { plane = value; }
		}
	}

	public class BspLeafNode : BspNode
	{
		private List<Brush> brushes = new List<Brush>();
		public List<Brush> Brushes
		{
			get { return brushes; }
		}

		public PvsCluster PvsCluster { get; set; }
	}
	#endregion

	public class BspPvs : PvsContent
	{
		public BspPvs()
		{
		}

		public uint[] PvsConnectivityBits { get; set; }
	}

	#region Entities
	//keep this in sync with the reader code
	public static class EntityTypeMap
	{
		private static Dictionary<Type, int> map = new Dictionary<Type, int>();

		static EntityTypeMap()
		{
			map.Add( typeof( Entity ), 0 );
			map.Add( typeof( LightEntity ), 1 );
			map.Add( typeof( WorldSpawnEntity ), 2 );
		}

		public static int GetTypeId( Entity ent )
		{
			int ret;
			if( !map.TryGetValue( ent.GetType(), out ret ) )
				throw new Exception();
			return ret;
		}
	}

	public static class EntityHelpers
	{
		public static Vector3 ParseVec3( string valueString )
		{
			float[] nums = ParseVecN( valueString, 3 );
			return new Vector3( nums[0], nums[1], nums[2] );
		}

		public static float[] ParseVecN( string valueString, int n )
		{
			string[] parts = valueString.Split( (char[])null, StringSplitOptions.RemoveEmptyEntries );
			if( parts.Length != n )
				throw new FormatException();

			float[] ret = new float[n];
			for( int i = 0; i < ret.Length; i++ )
			{
				ret[i] = float.Parse( parts[i] );
			}

			return ret;
		}

		public static Color ParseColor( string value )
		{
			return new Color( ParseVec3( value ) );
		}
	}

	public class Entity : BspItem
	{
		public Entity()
		{		
		}

		protected virtual bool ParseEntityProperty( string key, string value )
		{
			switch( key )
			{
			case EntityKeys.ClassName:
				className = value;
				return true;

			case EntityKeys.Origin:
				origin = EntityHelpers.ParseVec3( value );
				return true;

			case EntityKeys.Angle:
				angle = float.Parse( value );
				return true;

			default:
				return false;
			}
		}

		public void Initialize( Dictionary<string, string> values )
		{
			foreach( KeyValuePair<string, string> kvp in values )
			{
				if( !ParseEntityProperty( kvp.Key, kvp.Value ) )
					miscValues.Add( kvp.Key, kvp.Value );
			}
		}

		protected virtual void WriteValues( ContentWriter writer )
		{
			writer.Write( className );
			writer.Write( origin );
			writer.Write( (float)(angle * (Math.PI / 180)) );
			writer.WriteRawObject( miscValues );
		}

		internal void Write( ContentWriter writer )
		{
			WriteValues( writer );
		}

		private Dictionary<string, string> miscValues = new Dictionary<string,string>();
		/// <summary>
		/// Contains all values that don't have a propper property.
		/// </summary>
		public Dictionary<string, string> MiscValues
		{
			get { return miscValues; }
		}

		private string className;
		public string ClassName
		{
			get { return className; }
			set { className = value; }
		}

		private Vector3 origin;
		public Vector3 Origin
		{
			get { return origin; }
			set { origin = value; }
		}

		private float angle;
		public float Angle
		{
			get { return angle; }
			set { angle = value; }
		}

		private Entity target;
		public Entity Target
		{
			get { return target; }
			set { target = value; }
		}
	}

	public class WorldSpawnEntity : Entity
	{
		protected override bool ParseEntityProperty( string key, string value )
		{
			switch( key )
			{
			case EntityKeys.Message:
				message = value;
				return true;

			case EntityKeys.DeluxeMapping:
				deluxeMapping = int.Parse( value ) != 0;
				return true;

			default:
				return base.ParseEntityProperty( key, value );
			}
		}

		protected override void WriteValues( ContentWriter writer )
		{
			base.WriteValues( writer );
			writer.Write( message ?? string.Empty );
			writer.Write( deluxeMapping );
		}

		private string message;
		public string Message
		{
			get { return message; }
			set { message = value; }
		}

		private bool deluxeMapping;
		public bool DeluxeMapping
		{
			get { return deluxeMapping; }
			set { deluxeMapping = value; }
		}
	}

	public class LightEntity : Entity
	{
		protected override bool ParseEntityProperty( string key, string value )
		{
			switch( key )
			{
			case EntityKeys.Light:
				intensity = float.Parse( value );
				return true;

			case EntityKeys.Color:
				color = EntityHelpers.ParseColor( value );
				return true;

			default:
				return base.ParseEntityProperty( key, value );
			}
		}

		protected override void WriteValues( ContentWriter writer )
		{
			base.WriteValues( writer );
			writer.Write( color.PackedValue );
			writer.Write( intensity );
		}

		private Color color;
		public Color Color
		{
			get { return color; }
			set { color = value; }
		}

		private float intensity;
		public float Intensity
		{
			get { return intensity; }
			set { intensity = value; }
		}
	}
	#endregion

	public class CompiledBsp
	{
		public CompiledBsp() { }

		private List<Entity> entities = new List<Entity>();
		public List<Entity> Entities
		{
			get { return entities; }
		}

		private List<Texture2DContent> lightmaps = new List<Texture2DContent>();
		public List<Texture2DContent> Lightmaps
		{
			get { return lightmaps; }
		}

		private List<MaterialContent> materials = new List<MaterialContent>();
		public List<MaterialContent> Materials
		{
			get { return materials; }
		}

		private List<GeometryBuffer> buffers = new List<GeometryBuffer>();
		public List<GeometryBuffer> Buffers
		{
			get { return buffers; }
		}

		private List<Surface> surfaces = new List<Surface>();
		public List<Surface> Surfaces
		{
			get { return surfaces; }
		}

		private List<BspPartitionNode> partitionNodes = new List<BspPartitionNode>();
		public List<BspPartitionNode> PartitionNodes
		{
			get { return partitionNodes; }
		}

		private List<BspLeafNode> leafNodes = new List<BspLeafNode>();
		public List<BspLeafNode> LeafNodes
		{
			get { return leafNodes; }
		}

		public IEnumerable<BspNode> Nodes
		{
			get
			{
				foreach( BspNode leaf in leafNodes )
					yield return leaf;
				foreach( BspNode part in partitionNodes )
					yield return part;
			}
		}

		private List<Brush> brushes = new List<Brush>();
		public List<Brush> Brushes
		{
			get { return brushes; }
		}

		private BspPvs pvs;
		public BspPvs PvsData
		{
			get { return pvs; }
			set { pvs = value; }
		}
	}

}